﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

using CloudSoft.Extensions;

namespace ERPStore.Extensions
{
	public static partial class ModelsExtensions
	{
		public static string GetStatusText(this Models.OrderCart cart)
		{
			if (cart == null || cart.ItemCount == 0)
			{
				return "est vide";
			} 
			else if (cart.ItemCount == 1)
			{
				return "1 produit";
			}
			else
			{
				return string.Format("{0} produits", cart.ItemCount);
			}
		}

        public static bool IsConverted(this Models.QuoteStatus status)
        {
            if (status == ERPStore.Models.QuoteStatus.ConvertedToOrder
				|| status == ERPStore.Models.QuoteStatus.ManualyConvertedToOrder)
            {
                return true;
            }
            return false;
        }

		public static bool IsWaiting(this Models.QuoteStatus status)
		{
			if (status == ERPStore.Models.QuoteStatus.Waiting
				|| status == ERPStore.Models.QuoteStatus.WaitingPayement)
			{
				return true;
			}
			return false;
		}

		public static int GetPageCount(this Models.IPaginable pager)
		{
			return Convert.ToInt32(Math.Ceiling(pager.ItemCount / (pager.PageSize * 1.0)));
		}

		public static void Apply<T>(this Models.DateInterval interval, IQueryable<T> list)
			where T : class
		{
			if (interval == null
				|| interval.PropertyName.IsNullOrTrimmedEmpty())
			{
				return;
			}
			var typeParams = new ParameterExpression[] { Expression.Parameter(typeof(T), "") };
			var pi = typeof(T).GetProperty(interval.PropertyName);

			var lastMonthDate = DateTime.Today.AddMonths(-1);
			var lastYearDate = DateTime.Today.AddYears(-1);
			var lastWeekDate = DateTime.Today.AddDays(-7);

			DateTime firstDayOfLastWeek = lastWeekDate;
			while (firstDayOfLastWeek.DayOfWeek != System.DayOfWeek.Monday)
			{
				firstDayOfLastWeek = firstDayOfLastWeek.AddDays(-1);
			}

			DateTime fromDate = DateTime.MinValue;
			DateTime toDate = DateTime.MinValue;
			var param = Expression.Parameter(typeof(T), "x");
			BinaryExpression bodyFrom = null;
			BinaryExpression bodyTo = null;

			switch (interval.Period)
			{
				case Models.Period.FromStart:

					//fromDate = new DateTime(1995, 1, 1);
					//toDate = DateTime.Today.AddDays(1);

					//var param = Expression.Parameter(typeof(T), "x");
					//var lambda = Expression.Lambda<Func<T, bool>>(
					//    Expression.GreaterThanOrEqual(
					//        Expression.PropertyOrField(param, interval.PropertyName)
					//        , Expression.Constant(interval.ToDate, typeof(DateTime))
					//    ), param);


					break;
				case Models.Period.LastMonth:

					DateTime endOfLastMonth = new DateTime(lastMonthDate.Year, lastMonthDate.Month, 1).AddMonths(1).AddDays(-1);

					fromDate = new DateTime(lastMonthDate.Year, lastMonthDate.Month, 1);
					toDate = endOfLastMonth;

					break;

				case Models.Period.LastWeek:

					fromDate = firstDayOfLastWeek;
					toDate = firstDayOfLastWeek.AddDays(7);

					break;
				case Models.Period.ThisWeek:

					fromDate = firstDayOfLastWeek.AddDays(7);
					toDate = fromDate.AddDays(6);

					break;
				case Models.Period.LastYear:

					DateTime firstDayOfLastYear = new DateTime(lastYearDate.Year, 1, 1);

					fromDate = firstDayOfLastYear;
					toDate = new DateTime(lastYearDate.Year + 1, 1, 1).AddDays(-1);

					break;
				case Models.Period.SinceBeginOfMonth:

					fromDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
					toDate = DateTime.Today.AddDays(1).AddSeconds(-1);

					break;
				case Models.Period.SinceBeginOfYear:

					fromDate = new DateTime(DateTime.Now.Year, 1, 1);
					toDate = DateTime.Today.AddDays(1).AddSeconds(-1);

					break;
				case Models.Period.SinceOneMonth:

					DateTime lastMonth = DateTime.Now.AddDays(-31);
					fromDate = new DateTime(lastMonth.Year, lastMonth.Month, 1);
					toDate = DateTime.Now.AddDays(1);

					break;
				case Models.Period.SinceOneWeek:

					fromDate = firstDayOfLastWeek;
					toDate = DateTime.Now.AddDays(1);

					break;

				case Models.Period.SinceOneYear:
					DateTime lastYearDate2 = DateTime.Today.AddYears(-1);
					fromDate = new DateTime(lastYearDate2.Year, lastYearDate2.Month, 1);
					toDate = DateTime.Now.AddDays(1);
					break;

				case Models.Period.All:
					fromDate = new DateTime(1995, 1, 1);
					toDate = DateTime.Today.AddMonths(6);
					break;
				case Models.Period.Today:
					fromDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0);
					toDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59);
					break;
				case Models.Period.Yesterday:
					fromDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0).AddDays(-1);
					toDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59).AddDays(-1);
					break;
				case Models.Period.SinceThreeMonths:
					fromDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1, 0, 0, 0).AddMonths(-3);
					toDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59);
					break;
				case Models.Period.SevenDayAgo:
					fromDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 0, 0, 0).AddDays(-7);
					toDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59).AddDays(-7);
					break;
				case Models.Period.MonthOneYearAgo:
					fromDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1, 0, 0, 0).AddYears(-1);
					toDate = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1, 23, 59, 59).AddMonths(-11).AddDays(-1);
					break;

				case Models.Period.January:
				case Models.Period.February:
				case Models.Period.March:
				case Models.Period.Avril:
				case Models.Period.May:
				case Models.Period.June:
				case Models.Period.July:
				case Models.Period.August:
				case Models.Period.September:
				case Models.Period.October:
				case Models.Period.November:
				case Models.Period.December:
					var periodList = Enum.GetValues(typeof(Models.Period));
					var startDate = DateTime.Today.AddMonths(-1);
					while (true)
					{
						var p = (Models.Period)periodList.GetValue(startDate.Month + 20);
						if (p == interval.Period)
						{
							fromDate = new DateTime(startDate.Year, startDate.Month, 1);
							toDate = new DateTime(startDate.Year, startDate.Month, 1).AddMonths(1).AddDays(-1);
							break;
						}
						startDate = startDate.AddMonths(-1);
					}
					break;
				case Models.Period.Since24Hours:
					fromDate = DateTime.Now.AddHours(-24);
					toDate = DateTime.Now;
					break;
			}

			if (fromDate != DateTime.MinValue)
			{
				bodyFrom = Expression.GreaterThanOrEqual(
						Expression.PropertyOrField(param, interval.PropertyName)
						, Expression.Constant(fromDate, typeof(DateTime)));

				list = list.Where(Expression.Lambda<Func<T, bool>>(bodyFrom, param));
			}

			if (toDate != DateTime.MinValue)
			{
				bodyTo = Expression.LessThanOrEqual(
						Expression.PropertyOrField(param, interval.PropertyName)
						, Expression.Constant(toDate, typeof(DateTime)));

				list = list.Where(Expression.Lambda<Func<T, bool>>(bodyTo, param));
			}
		}

		public static DateTime? GetNextShippingDate(this Models.OrderCart cart)
		{
			var productStockInfoList = cart.Items.Where(i => i.ProductStockInfo != null)
												.Select(i => i.ProductStockInfo);

			if (productStockInfoList.IsNullOrEmpty())
			{
				return null;
			}

			var maxDate = DateTime.MinValue;
			foreach (var item in productStockInfoList)
			{
				var next = item.GetNextShippingDate();
				if (next.HasValue
					&& next.Value > maxDate)
				{
					maxDate = next.Value;
				}
			}

			if (maxDate == DateTime.MinValue)
			{
				return null;
			}

			return maxDate;
		}

		public static Models.Price ToUnitPrice(this Models.Price input, int unitValue)
		{
			if (unitValue == 0)
			{
				throw new ArgumentException("unitValue must be different of zero");
			}
			var result = new Models.Price(input.Value / Convert.ToDecimal(unitValue), input.TaxRate);
			var parts = result.Value.ToString().Split(',');
			/*result.IntegerPart = parts.First();
			result.DecimalPart = parts.Last();

			parts = result.ValueWithTax.ToString().Split(',');
			result.IntegerPartWitTax = parts.First();
			result.DecimalPartWithTax = parts.Last();*/
			return result;
		}

		/// <summary>
		/// Remplissage des propriétés d'une class implementant ISettings avec le fichier de configuration
		/// </summary>
		/// <param name="model">The model.</param>
		/// <param name="nvc">The NVC.</param>
		public static void BindFromConfiguration(this object model, System.Collections.Specialized.NameValueCollection nvc)
		{
			if (model == null
				|| nvc == null)
			{
				return;
			}

			var propertyInfoList = model.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty);
			foreach (var propertyInfo in propertyInfoList)
			{
				if (!propertyInfo.CanWrite)
				{
					continue;
				}

				var key = nvc.AllKeys.SingleOrDefault(i => i.Equals(propertyInfo.Name, StringComparison.InvariantCultureIgnoreCase));
				if (key == null)
				{
					continue;
				}

				var value = nvc[key];
				if (value == null)
				{
					continue;
				}

				var typedValue = System.Convert.ChangeType(value, propertyInfo.PropertyType);
				propertyInfo.SetValue(model, typedValue, null);
			}
		}

		public static string ToFullReason(this IList<ERPStore.Models.BrokenRule> brokenRules)
		{
			if (brokenRules.IsNullOrEmpty())
			{
				return null;
			}

			var result = string.Empty;
			foreach (var item in brokenRules)
			{
				foreach (var rule in item.ErrorList)
				{
					result += string.Format("{0}:{1}{2}", item.PropertyName, rule, System.Environment.NewLine);
				}
			}
			return result;
		}

		public static string ToJSon(this object o)
		{
			var jsonSerializer = new Newtonsoft.Json.JsonSerializer();
			var sb = new StringBuilder();
			var textWriter = new System.IO.StringWriter(sb);
			var jsonWriter = new Newtonsoft.Json.JsonTextWriter(textWriter);
			jsonSerializer.Serialize(jsonWriter, o);
			return sb.ToString();
		}

		public static T FromJSon<T>(this string json)
			where T : class
		{
			var jsonSerializer = new Newtonsoft.Json.JsonSerializer();
			var textReader = new System.IO.StringReader(json);
			var jsonReader = new Newtonsoft.Json.JsonTextReader(textReader);
			return jsonSerializer.Deserialize<T>(jsonReader);
		}


		public static string GetFileNameFromUrl(this string uri)
		{
			string fileName = System.IO.Path.GetFileName(uri);
			var parts = fileName.Split('?');
			if (parts.Length > 0)
			{
				fileName = parts[0];
			}
			return fileName;
		}

		public static string GetExtension(this Models.Media item, string defaultExtension = ".png")
		{
			string ext = defaultExtension;

			if (item.MimeType == null
				&& !item.ExternalUrl.IsNullOrTrimmedEmpty())
			{
				string fileName = null;
				try
				{
					fileName = item.ExternalUrl.GetFileNameFromUrl();
				}
				catch
				{
				}

				if (!fileName.IsNullOrTrimmedEmpty())
				{
					ext = System.IO.Path.GetExtension(fileName);
				}
			}
			else if (item.MimeType != null)
			{
				var mimeType = Extensions.MimeExtensions.MIMETypeDictionary.FirstOrDefault(i => i.Value.Equals(item.MimeType, StringComparison.InvariantCultureIgnoreCase));
				if (mimeType.Key != null)
				{
					ext = "." + mimeType.Key;
				}
			}
			else if (!item.FileName.IsNullOrEmpty())
			{
				ext = System.IO.Path.GetExtension(item.FileName);
			}

			return ext;
		}

		public static string ToLocalizedName(this Models.ProductRelationType relationType)
		{
			switch (relationType)
			{
				case ERPStore.Models.ProductRelationType.Similar:
					return "Les produits similaires";
				case ERPStore.Models.ProductRelationType.Complementary:
					return "Les produits complémentaires";
				case ERPStore.Models.ProductRelationType.Variant:
					return "Les variations";
				case ERPStore.Models.ProductRelationType.Substitute:
					return "Les produits substituables";
				default:
					break;
			}
			return null;
		}

		public static Dictionary<string, string> ToDictionary(this IEnumerable<Models.NameValueParameter> list)
		{
			var result = new Dictionary<string, string>();
			foreach (var item in list)
			{
				result.Add(item.Name, item.Value);
			}
			return result;
		}

		public static ERPStore.Models.WebParameter GetUriParameter(this IEnumerable<ERPStore.Models.WebParameter> list, string name)
		{
			if (list == null)
			{
				return null;
			}
			if (name == null)
			{
				return null;
			}
			return list.Where(i => i.Type == Models.WebParameterType.Uri).SingleOrDefault(i => i.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
		}

		public static string GetUriParameterValue(this IEnumerable<ERPStore.Models.WebParameter> list, string name)
		{
			var parameter = list.GetUriParameter(name);
			if (parameter != null)
			{
				return parameter.Value;
			}
			return null;
		}

		public static System.Collections.Specialized.NameValueCollection ToUriNameValueCollection(this IEnumerable<ERPStore.Models.WebParameter> list)
		{
			var result = new System.Collections.Specialized.NameValueCollection();

			foreach (var item in list.Where(i => i.Type == Models.WebParameterType.Uri).Distinct(i => i.Name))
			{
				result.Add(item.Name, item.Value);
			}

			return result;
		}

		public static string GetHost(this IEnumerable<ERPStore.Models.WebParameter> list)
		{
			var result = list.FirstOrDefault(i => i.Type == Models.WebParameterType.Domain && i.Name == "host");
			if (result != null)
			{
				return result.Name;
			}
			return null;
		}

		public static IList<T> ToClonedList<T>(this IEnumerable<T> list)
			where T : ICloneable
		{
			var clonedList = new List<T>();
			foreach (var item in list)
			{
				clonedList.Add((T)item.Clone());
			}
			return clonedList;
		}



	}
}
